// OpenGLView.cpp : implementation file
//

#include "stdafx.h"
#include "sharks.h"
#include "OpenGLView.h"
#include ".\openglview.h"


// COpenGLView

IMPLEMENT_DYNCREATE(COpenGLView, CView)

COpenGLView::COpenGLView()
{
}

COpenGLView::~COpenGLView()
{
}

BEGIN_MESSAGE_MAP(COpenGLView, CView)
	ON_WM_SIZE()
	ON_WM_CREATE()
	ON_WM_ERASEBKGND()
	ON_WM_DESTROY()
//	ON_COMMAND(ID_NAVIGATION_FOLLOWNEXTSHARK, OnNavigationFollownextshark)
END_MESSAGE_MAP()


// COpenGLView drawing

/*
 *	There is a clear sequence of events that occurs in OpenGL programs when it 
 *	is time to re-render the scene. These are:
 *	-Clear the buffers 
 *	-Render the scene 
 *	-Flush the rendering pipeline 
 *	-Swap the contents of the back buffer if double buffering is being used. 
 */
void COpenGLView::OnDraw(CDC* pDC)
{
	// CDocument* pDoc = GetDocument();
	

	// Clear out the color & depth buffers
	glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

	RenderScene();

	// Tell OpenGL to flush its pipeline
    // glFinish();

	// Now Swap the buffers
	SwapBuffers( m_pDC->GetSafeHdc() );
}


// COpenGLView diagnostics

#ifdef _DEBUG
void COpenGLView::AssertValid() const
{
	CView::AssertValid();
}

void COpenGLView::Dump(CDumpContext& dc) const
{
	CView::Dump(dc);
}
#endif //_DEBUG


// COpenGLView message handlers


/*
 *	OnSize() is where we usually set up the Viewport and Viewing Frustum 
 *	because they depend on the size of the Window. Thus the viewport and 
 *	viewing frustum are setup initially when OnSize() is called and 
 *	subsequently during every resize of the Window that occurs. The basic 
 *	operations that occur are setting up the viewport, selecting the Projection 
 *	matrix, initializing it  and setting up the viewing frustum. The last 
 *	operations are selecting the Modelview matrix, initializing it and setting 
 *	up the viewing transformations. 
 */
void COpenGLView::OnSize(UINT nType, int cx, int cy)
{
	CView::OnSize(nType, cx, cy);

	// Declare a variable to store the aspect ratio i.e the width to 
	// height ratio of the Window as the viewing frustum depends on this.
	GLdouble aspect_ratio; // width/height ratio

	// Return if the either the width or the height of the Window is 0
	if ( 0 >= cx || 0 >= cy )
	{
		// no need to do anything, since nothing is visible...
		return;
	}

	// The glViewport function sets the viewport. Compute the aspect ratio, 
	// select the projection matrix and clear it. Set the Viewing Volume. 
	// This means that all further commands will affect the projection matrix.
	// select the full client area
	glViewport(0, 0, cx, cy);

	// compute the aspect ratio
	// this will keep all dimension scales equal
	aspect_ratio = (GLdouble)cx/(GLdouble)cy;

	// select the projection matrix and clear it
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();

	// select the viewing volume
	// TODO give more control to the user of the class about the projection
	// so he doesn't need to override this method
	gluPerspective(60.0f, aspect_ratio, 0.1f, 200.0f);

	// switch back to the modelview matrix and clear it
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
}

/*
 *	we call the OpenGL initialization code here and not in the constructor
 *	to be sure that an appropriate dc is present
 */
int COpenGLView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
	if (CView::OnCreate(lpCreateStruct) == -1)
		return -1;

	//AfxMessageBox("COpenGLView::OnCreate");

	InitializeOpenGL();

	return 0;
}

/*
 *	Since we have OpenGL doing the job of clearing the background, we'll 
 *	turn off Windows from clearing the background. This can be done by 
 *	editing OnEraseBkgnd() member function appropriately. We do this 
 *	by returning true from the function.
 */
BOOL COpenGLView::OnEraseBkgnd(CDC* pDC)
{
	// return CView::OnEraseBkgnd(pDC);
	return TRUE;	// or FALSE, doesn't make any difference
}


/*
 *	we have to explicitly de-allocate the CClientDC memory by using the 
 *	delete operator. 
 *	we also clean-up everything else
 */
void COpenGLView::OnDestroy()
{
	TRACE("COpenGLView::OnDestroy");
	CView::OnDestroy();

	//Make the RC non-current
	if(::wglMakeCurrent (0,0) == FALSE)
	{
		AfxMessageBox("Could not make RC non-current");
	}

	//Delete the rendering context
	if(::wglDeleteContext (m_hRC)==FALSE)
	{
		AfxMessageBox("Could not delete RC");
	}

	//Delete the DC
	if(m_pDC)
	{
		delete m_pDC;
	}

	//Set it to NULL
	m_pDC = NULL;
}


/*
 *	OpenGL initialization code
 */
BOOL COpenGLView::InitializeOpenGL()
{
	// *** mandatory statements***
	// the following lines must be called before using opengl

	// we first obtain a DC to the client area and check 
	// to make sure if that is not NULL. 
	m_pDC = new CClientDC(this);	//Get a DC for the Client Area
	if(m_pDC == NULL)				
	{
		//Failure to Get DC
		AfxMessageBox("Error Obtaining DC");
		return FALSE;
	}

	// then we call SetupPixelFormat() to set up the pixel 
	// format and checks for failure
	if(!SetupPixelFormat())
	{
		AfxMessageBox("Failure to set the pixel format");
		return FALSE;
	}

	// then we create a rendering context from the DC obtained and check 
	// for failure. The wglCreateContext function creates a new OpenGL 
	// rendering context, which is suitable for drawing on the device 
	// referenced by DC.
	m_hRC = ::wglCreateContext (m_pDC->GetSafeHdc ()); //Create Rendering Context
	if(m_hRC == 0)
	{
		//Failure to Create Rendering Context
		AfxMessageBox("Error Creating RC");
		return FALSE;
	}

	// Then, we make the RC created the current. The wglMakeCurrent 
	// function makes a specified OpenGL rendering context the calling 
	// thread's current rendering context. All subsequent OpenGL calls 
	// made by the thread are drawn on the device identified by DC. 
	if(::wglMakeCurrent (m_pDC->GetSafeHdc (), m_hRC)==FALSE)	//Make the RC Current
	{
		AfxMessageBox("Error making RC Current");
		return FALSE;
	}
	
	//*** End of mandatory statements***
	// from here, everything can be called from the derived class 

	// initialize certain OpenGL State Variables in the next lines of 
	// code. OpenGL maintains certain global state variables that store 
	// information regarding the current OpenGL state such as current background 
	// color, current color, lighting information, material property etc. exactly 
	// the way the Windows GDI maintains information such as the current brush, 
	// current pen  etc. 

	/*glClearColor(0.3f,0.3f,0.3f,0.0f);		
	glClearDepth(1.0f);						
	glEnable(GL_DEPTH_TEST);				*/

	return TRUE;
}




BOOL COpenGLView::SetupPixelFormat()
{
	static PIXELFORMATDESCRIPTOR pfd = 
	{
		sizeof(PIXELFORMATDESCRIPTOR),			// size of this pfd
			1,									// version number
			PFD_DRAW_TO_WINDOW |				// support window
			PFD_SUPPORT_OPENGL |                // support OpenGL
			PFD_DOUBLEBUFFER,                   // double buffered
			PFD_TYPE_RGBA,                      // RGBA type
			24,                                 // 24-bit color depth
			0, 0, 0, 0, 0, 0,                   // color bits ignored
			0,                                  // no alpha buffer
			0,									// shift bit ignored
			0,                                  // no accumulation buffer
			0, 0, 0, 0,                         // accumulation bits ignored
			16,                                 // 16-bit z-buffer
			0,                                  // no stencil buffer
			0,                                  // no auxiliary buffer
			PFD_MAIN_PLANE,                     // main layer
			0,                                  // reserved
			0, 0, 0                             // layer masks ignored
	};

	// Once the pixel format has been set we try to get the closest 
	// pixel format available by calling the ChoosePixelFormat function. 
	// The ChoosePixelFormat function attempts to match an appropriate 
	// pixel format supported by a device context to a given pixel format 
	// specification. We also check to ensure that the index returned is 
	// non-zero.
	int m_nPixelFormat = ::ChoosePixelFormat(m_pDC->GetSafeHdc(), &pfd);

	if ( m_nPixelFormat == 0 )
	{
		return FALSE;
	}

	// Once we obtain the closest match, we call the SetPixelFormat function 
	// which sets the pixel format of the specified device context to the 
	// format specified by the index. We also check to ensure that it worked properly.
	if ( ::SetPixelFormat(m_pDC->GetSafeHdc(), m_nPixelFormat, &pfd) == FALSE)
	{
		return FALSE;
	}

	// If everything has worked properly the function returns true.
	return TRUE;
}


/*
 *	all OpenGL drawing goes here
 */
void COpenGLView::RenderScene()
{
	// sample drawing here

	glBegin(GL_TRIANGLES);
	{
		glColor3f(1,0,0);
		glVertex3f(-1,-1,-3);
		glColor3f(0,1,0);
		glVertex3f(0,1,-3);
		glColor3f(0,0,1);
		glVertex3f(1,-1,-3);
	}
	glEnd();

	// start immediately drawing the next frame
	// Invalidate(FALSE);	
}


/*
 *	Before the Window is created we have to set the Window 
 *  style to include WS_CLIPCHILDREN and WS_CLIPSIBLINGS to 
 *	prevent OpenGL from trying to draw into any other Windows. 
 *	We should make these changes to the PreCreateWindow() 
 *	member function.
 */
BOOL COpenGLView::PreCreateWindow(CREATESTRUCT& cs)
{
	cs.style |= WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
	return CView::PreCreateWindow(cs);
}


/*
 *	set clear color for OpenGL
 *	useful when using color dialogs to change the 
 */
void COpenGLView::SetClearColor(COLORREF color, float alpha/* =1.0f */)
{
	float r = (float)GetRValue(color)/255.0f;
	float g = (float)GetGValue(color)/255.0f;
	float b = (float)GetBValue(color)/255.0f;
	::glClearColor(r, g, b, alpha);
}


void COpenGLView::SetClearColor(float r,float g,float b,float alpha/* =1.0f */)
{
	::glClearColor(r, g, b, alpha);
}

void COpenGLView::SetClearColor(float color[4])
{
	::glClearColor(color[0], color[1], color[2], color[3]);
}
//void COpenGLView::OnNavigationFollownextshark()
//{
//	// TODO: Add your command handler code here
//}
